home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / _archvrs / dos / source / aspitape / diffarch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-01-26  |  16.1 KB  |  695 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  *
  25.  * @(#) diffarch.c 1.10 87/11/11 - gnu
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <errno.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #endif
  36.  
  37. #ifndef MSDOS
  38. #include <sys/ioctl.h>
  39. #if !defined(USG) || defined(HAVE_MTIO)
  40. #include <sys/mtio.h>
  41. #endif
  42. #else
  43. #include <fcntl.h>
  44. #endif
  45.  
  46. #ifdef USG
  47. #include <fcntl.h>
  48. #endif
  49.  
  50. /* Some systems don't have these #define's -- we fake it here. */
  51. #ifndef O_RDONLY
  52. #define    O_RDONLY    0
  53. #endif
  54. #ifndef    O_NDELAY
  55. #define    O_NDELAY    0
  56. #endif
  57.  
  58. #ifndef S_IFLNK
  59. #define lstat stat
  60. #endif
  61.  
  62. extern int errno;            /* From libc.a */
  63. extern char *valloc();            /* From libc.a */
  64.  
  65. #include "tar.h"
  66. #include "port.h"
  67. #include "rmt.h"
  68.  
  69. extern union record *head;        /* Points to current tape header */
  70. extern struct stat hstat;        /* Stat struct corresponding */
  71. extern int head_standard;        /* Tape header is in ANSI format */
  72.  
  73. extern void print_header();
  74. extern void skip_file();
  75. extern void skip_extended_headers();
  76.  
  77. extern FILE *msg_file;
  78.  
  79. int now_verifying = 0;        /* Are we verifying at the moment? */
  80.  
  81. int    diff_fd;        /* Descriptor of file we're diffing */
  82.  
  83. char    *diff_buf = 0;        /* Pointer to area for reading
  84.                        file contents into */
  85.  
  86. char    *diff_dir;        /* Directory contents for LF_DUMPDIR */
  87.  
  88. int different = 0;
  89.  
  90. /*struct sp_array *sparsearray;
  91. int         sp_ar_size = 10;*/
  92. /*
  93.  * Initialize for a diff operation
  94.  */
  95. diff_init()
  96. {
  97.  
  98.     /*NOSTRICT*/
  99.     diff_buf = (char *) valloc((unsigned)blocksize);
  100.     if (!diff_buf) {
  101.         msg("could not allocate memory for diff buffer of %d bytes",
  102.             blocksize);
  103.         exit(EX_ARGSBAD);
  104.     }
  105. }
  106.  
  107. /*
  108.  * Diff a file against the archive.
  109.  */
  110. void
  111. diff_archive()
  112. {
  113.     register char *data;
  114.     int check, namelen;
  115.     int err;
  116.     long offset;
  117.     struct stat filestat;
  118.     int compare_chunk();
  119.     int compare_dir();
  120. #ifndef __MSDOS__
  121.     dev_t    dev;
  122.     ino_t    ino;
  123. #endif
  124.     char *get_dir_contents();
  125.     long from_oct();
  126.     long lseek();
  127.  
  128.     errno = EPIPE;            /* FIXME, remove perrors */
  129.  
  130.     saverec(&head);            /* Make sure it sticks around */
  131.     userec(head);            /* And go past it in the archive */
  132.     decode_header(head, &hstat, &head_standard, 1);    /* Snarf fields */
  133.  
  134.     /* Print the record from 'head' and 'hstat' */
  135.     if (f_verbose) {
  136.         if(now_verifying)
  137.             fprintf(msg_file,"Verify ");
  138.         print_header();
  139.     }
  140.  
  141.     switch (head->header.linkflag) {
  142.  
  143.     default:
  144.         msg("Unknown file type '%c' for %s, diffed as normal file",
  145.             head->header.linkflag, head->header.name);
  146.         /* FALL THRU */
  147.  
  148.     case LF_OLDNORMAL:
  149.     case LF_NORMAL:
  150.     case LF_SPARSE:
  151.     case LF_CONTIG:
  152.         /*
  153.          * Appears to be a file.
  154.          * See if it's really a directory.
  155.          */
  156.         namelen = strlen(head->header.name)-1;
  157.         if (head->header.name[namelen] == '/')
  158.             goto really_dir;
  159.  
  160.         
  161.         if(do_stat(&filestat)) {
  162.             if (head->header.isextended)
  163.                 skip_extended_headers();
  164.             skip_file((long)hstat.st_size);
  165.             different++;
  166.             goto quit;
  167.         }
  168.  
  169.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  170.             fprintf(msg_file, "%s: not a regular file\n",
  171.                 head->header.name);
  172.             skip_file((long)hstat.st_size);
  173.             different++;
  174.             goto quit;
  175.         }
  176.  
  177.         filestat.st_mode &= ~S_IFMT;
  178.         if (filestat.st_mode != hstat.st_mode)
  179.             sigh("mode");
  180.         if (filestat.st_uid  != hstat.st_uid)
  181.             sigh("uid");
  182.         if (filestat.st_gid  != hstat.st_gid)
  183.             sigh("gid");
  184.         if (filestat.st_mtime != hstat.st_mtime)
  185.             sigh("mod time");
  186.         if (head->header.linkflag != LF_SPARSE &&
  187.                 filestat.st_size != hstat.st_size) {
  188.             sigh("size");
  189.             skip_file((long)hstat.st_size);
  190.             goto quit;
  191.         }
  192.  
  193.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  194. #ifdef __MSDOS__
  195.                 setmode(diff_fd, O_BINARY);
  196. #endif
  197.         if (diff_fd < 0 && !f_absolute_paths) {
  198.             char tmpbuf[NAMSIZ+2];
  199.  
  200.             tmpbuf[0]='/';
  201.             strcpy(&tmpbuf[1],head->header.name);
  202.             diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY);
  203.         }
  204.         if (diff_fd < 0) {
  205.             msg_perror("cannot open %s",head->header.name);
  206.             if (head->header.isextended)
  207.                 skip_extended_headers();
  208.             skip_file((long)hstat.st_size);
  209.             different++;
  210.             goto quit;
  211.         }
  212.         /*
  213.          * Need to treat sparse files completely differently here.
  214.          */
  215.         if (head->header.linkflag == LF_SPARSE)
  216.             diff_sparse_files(hstat.st_size);
  217.         else 
  218.             wantbytes((long)(hstat.st_size),compare_chunk);
  219.  
  220.         check = close(diff_fd);
  221.         if (check < 0)
  222.             msg_perror("Error while closing %s",head->header.name);
  223.  
  224.     quit:
  225.         break;
  226.  
  227. #ifndef __MSDOS__
  228.     case LF_LINK:
  229.         if(do_stat(&filestat))
  230.             break;
  231.         dev = filestat.st_dev;
  232.         ino = filestat.st_ino;
  233.         err = stat(head->header.linkname, &filestat);
  234.         if (err < 0) {
  235.             if (errno==ENOENT) {
  236.                 fprintf(msg_file, "%s: does not exist\n",head->header.name);
  237.             } else {
  238.                 msg_perror("cannot stat file %s",head->header.name);
  239.             }
  240.             different++;
  241.             break;
  242.         }
  243.         if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
  244.             fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname);
  245.             break;
  246.         }
  247.         break;
  248. #endif
  249.  
  250. #ifdef S_IFLNK
  251.     case LF_SYMLINK:
  252.     {
  253.         char linkbuf[NAMSIZ+3];
  254.         check = readlink(head->header.name, linkbuf,
  255.                  (sizeof linkbuf)-1);
  256.         
  257.         if (check < 0) {
  258.             if (errno == ENOENT) {
  259.                 fprintf(msg_file,
  260.                     "%s: no such file or directory\n",
  261.                     head->header.name);
  262.             } else {
  263.                 msg_perror("cannot read link %s",head->header.name);
  264.             }
  265.             different++;
  266.             break;
  267.         }
  268.  
  269.         linkbuf[check] = '\0';    /* Null-terminate it */
  270.         if (strncmp(head->header.linkname, linkbuf, check) != 0) {
  271.             fprintf(msg_file, "%s: symlink differs\n",
  272.                 head->header.linkname);
  273.             different++;
  274.         }
  275.     }
  276.         break;
  277. #endif
  278.  
  279.     case LF_CHR:
  280.         hstat.st_mode |= S_IFCHR;
  281.         goto check_node;
  282.  
  283. #ifdef S_IFBLK
  284.     /* If local system doesn't support block devices, use default case */
  285.     case LF_BLK:
  286.         hstat.st_mode |= S_IFBLK;
  287.         goto check_node;
  288. #endif
  289.  
  290. #ifdef S_IFIFO
  291.     /* If local system doesn't support FIFOs, use default case */
  292.     case LF_FIFO:
  293.         hstat.st_mode |= S_IFIFO;
  294.         hstat.st_rdev = 0;        /* FIXME, do we need this? */
  295.         goto check_node;
  296. #endif
  297.  
  298.     check_node:
  299.         /* FIXME, deal with umask */
  300.         if(do_stat(&filestat))
  301.             break;
  302.         if(hstat.st_rdev != filestat.st_rdev) {
  303.             fprintf(msg_file, "%s: device numbers changed\n", head->header.name);
  304.             different++;
  305.             break;
  306.         }
  307.         if(hstat.st_mode != filestat.st_mode) {
  308.             fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name);
  309.             different++;
  310.             break;
  311.         }
  312.         break;
  313.  
  314.     case LF_DUMPDIR:
  315.         data=diff_dir=get_dir_contents(head->header.name,0);
  316.         wantbytes((long)(hstat.st_size),compare_dir);
  317.         free(data);
  318.         /* FALL THROUGH */
  319.  
  320.     case LF_DIR:
  321.         /* Check for trailing / */
  322.         namelen = strlen(head->header.name)-1;
  323.     really_dir:
  324.         while (namelen && head->header.name[namelen] == '/')
  325.             head->header.name[namelen--] = '\0';    /* Zap / */
  326.  
  327.         if(do_stat(&filestat))
  328.             break;
  329.         if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
  330.             fprintf(msg_file, "%s is no longer a directory\n",head->header.name);
  331.             different++;
  332.             break;
  333.         }
  334.         if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
  335.             sigh("mode");
  336.         break;
  337.  
  338.     case LF_VOLHDR:
  339.         break;
  340.  
  341.     case LF_MULTIVOL:
  342.         namelen = strlen(head->header.name)-1;
  343.         if (head->header.name[namelen] == '/')
  344.             goto really_dir;
  345.  
  346.         if(do_stat(&filestat))
  347.             break;
  348.  
  349.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  350.             fprintf(msg_file, "%s: not a regular file\n",
  351.                 head->header.name);
  352.             skip_file((long)hstat.st_size);
  353.             different++;
  354.             break;
  355.         }
  356.  
  357.         filestat.st_mode &= ~S_IFMT;
  358.         offset = from_oct(1+12, head->header.offset);
  359.         if (filestat.st_size != hstat.st_size + offset) {
  360.             sigh("size");
  361.             skip_file((long)hstat.st_size);
  362.             different++;
  363.             break;
  364.         }
  365.  
  366. #ifndef __MSDOS__
  367.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  368. #else
  369.                 diff_fd = open(head->header.name, O_NDELAY|O_RDONLY|O_BINARY);
  370. #endif
  371.  
  372.         if (diff_fd < 0) {
  373.             msg_perror("cannot open file %s",head->header.name);
  374.             skip_file((long)hstat.st_size);
  375.             different++;
  376.             break;
  377.         }
  378.         err = lseek(diff_fd, offset, 0);
  379.         if(err!=offset) {
  380.             msg_perror("cannot seek to %ld in file %s",offset,head->header.name);
  381.             different++;
  382.             break;
  383.         }
  384.  
  385.         wantbytes((long)(hstat.st_size),compare_chunk);
  386.  
  387.         check = close(diff_fd);
  388.         if (check < 0) {
  389.             msg_perror("Error while closing %s",head->header.name);
  390.         }
  391.         break;
  392.  
  393.     }
  394.  
  395.     /* We don't need to save it any longer. */
  396.     saverec((union record **) 0);    /* Unsave it */
  397. }
  398.  
  399. int
  400. compare_chunk(bytes,buffer)
  401. long bytes;
  402. char *buffer;
  403. {
  404.     int err;
  405.  
  406.         err=read(diff_fd,diff_buf,( unsigned ) bytes);
  407.         if(err!=( int ) bytes) {
  408.         if(err<0) {
  409.             msg_perror("can't read %s",head->header.name);
  410.         } else {
  411.                         fprintf(msg_file,"%s: could only read %d of %ld bytes\n",head->header.name,err,bytes);
  412.         }
  413.         different++;
  414.         return -1;
  415.     }
  416.         if(bcmp(buffer,diff_buf,( unsigned ) bytes)) {
  417.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  418.         different++;
  419.         return -1;
  420.     }
  421.     return 0;
  422. }
  423.  
  424. int
  425. compare_dir(bytes,buffer)
  426. long bytes;
  427. char *buffer;
  428. {
  429.         if(bcmp(buffer,diff_dir,( unsigned ) bytes)) {
  430.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  431.         different++;
  432.         return -1;
  433.     }
  434.     diff_dir+=bytes;
  435.     return 0;
  436. }
  437.  
  438. /*
  439.  * Sigh about something that differs.
  440.  */
  441. sigh(what)
  442.     char *what;
  443. {
  444.  
  445.     fprintf(msg_file, "%s: %s differs\n",
  446.         head->header.name, what);
  447. }
  448.  
  449. verify_volume()
  450. {
  451.     int status;
  452. #ifdef MTIOCTOP
  453.     struct mtop t;
  454.     int er;
  455. #endif
  456.  
  457.     if(!diff_buf)
  458.         diff_init();
  459. #ifdef MTIOCTOP
  460.     t.mt_op = MTBSF;
  461.     t.mt_count = 1;
  462.     if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  463.         if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  464. #endif
  465.             if(rmtlseek(archive,0L,0)!=0) {
  466.                 /* Lseek failed.  Try a different method */
  467.                 msg_perror("Couldn't rewind archive file for verify");
  468.                 return;
  469.             }
  470. #ifdef MTIOCTOP
  471.         }
  472.     }
  473. #endif
  474.     ar_reading=1;
  475.     now_verifying = 1;
  476.     fl_read();
  477.     for(;;) {
  478.         status = read_header();
  479.         if(status==0) {
  480.             unsigned n;
  481.  
  482.             n=0;
  483.             do {
  484.                 n++;
  485.                 status=read_header();
  486.             } while(status==0);
  487.             msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
  488.         }
  489.         if(status==2 || status==EOF)
  490.             break;
  491.         diff_archive();
  492.     }
  493.     ar_reading=0;
  494.     now_verifying = 0;
  495.  
  496. }
  497.  
  498. int do_stat(statp)
  499. struct stat *statp;
  500. {
  501.     int err;
  502.  
  503.     err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp);
  504.     if (err < 0) {
  505.         if (errno==ENOENT) {
  506.             fprintf(msg_file, "%s: does not exist\n",head->header.name);
  507.         } else
  508.             msg_perror("can't stat file %s",head->header.name);
  509. /*        skip_file((long)hstat.st_size);
  510.         different++;*/
  511.         return 1;
  512.     } else
  513.         return 0;
  514. }
  515.  
  516. /*
  517.  * JK
  518.  * Diff'ing a sparse file with its counterpart on the tar file is a 
  519.  * bit of a different story than a normal file.  First, we must know
  520.  * what areas of the file to skip through, i.e., we need to contruct
  521.  * a sparsearray, which will hold all the information we need.  We must
  522.  * compare small amounts of data at a time as we find it.  
  523.  */
  524.  
  525. diff_sparse_files(filesize)
  526. int    filesize;
  527.  
  528. {
  529.     int        sparse_ind = 0;
  530.     char        *buf;
  531.     int        buf_size = RECORDSIZE;
  532.     union record     *datarec;    
  533.     int        err;
  534.     long        numbytes;
  535.     int        amt_read = 0;
  536.     int        size = filesize;
  537.  
  538.     buf = (char *) malloc(buf_size * sizeof (char));
  539.     
  540.     fill_in_sparse_array();
  541.     
  542.  
  543.     while (size > 0) {
  544.         datarec = findrec();
  545.         if (!sparsearray[sparse_ind].numbytes)
  546.             break;
  547.  
  548.         /*
  549.          * 'numbytes' is nicer to write than
  550.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  551.          */
  552.         numbytes = sparsearray[sparse_ind].numbytes;
  553.         
  554.         lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
  555.         /*
  556.          * take care to not run out of room in our buffer
  557.          */
  558.         while (buf_size < numbytes) {
  559.             buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
  560.             buf_size *= 2;
  561.         }
  562.         while (numbytes > RECORDSIZE) {
  563.             if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
  564.                  if (err < 0) 
  565.                     msg_perror("can't read %s", head->header.name);
  566.                 else
  567.                                         fprintf(msg_file, "%s: could only read %d of %ld bytes\n",
  568.                         err, numbytes);
  569.                 break;
  570.             }
  571.             if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
  572.                 different++;
  573.                 break;
  574.             }
  575.             numbytes -= err;
  576.             size -= err;
  577.             userec(datarec);
  578.             datarec = findrec();
  579.         }
  580.                 if ((err = read(diff_fd, buf, ( unsigned ) numbytes)) != (int) numbytes) {
  581.              if (err < 0) 
  582.                 msg_perror("can't read %s", head->header.name);
  583.             else
  584.                                 fprintf(msg_file, "%s: could only read %d of %ld bytes\n",
  585.                         err, numbytes);
  586.             break;
  587.         }
  588.  
  589.                 if (bcmp(buf, datarec->charptr, ( unsigned ) numbytes)) {
  590.             different++;
  591.             break;
  592.         }
  593. /*        amt_read += numbytes;
  594.         if (amt_read >= RECORDSIZE) {
  595.             amt_read = 0;
  596.             userec(datarec);
  597.             datarec = findrec();
  598.         }*/
  599.         userec(datarec);
  600.         sparse_ind++;
  601.         size -= numbytes;
  602.     }
  603.     /* 
  604.      * if the number of bytes read isn't the
  605.      * number of bytes supposedly in the file,
  606.      * they're different
  607.      */
  608. /*    if (amt_read != filesize)
  609.         different++;*/
  610.     userec(datarec);
  611.     free(sparsearray);
  612.     if (different)
  613.         fprintf(msg_file, "%s: data differs\n", head->header.name);
  614.  
  615. }
  616.  
  617. /*
  618.  * JK
  619.  * This routine should be used more often than it is ... look into
  620.  * that.  Anyhow, what it does is translate the sparse information
  621.  * on the header, and in any subsequent extended headers, into an
  622.  * array of structures with true numbers, as opposed to character
  623.  * strings.  It simply makes our life much easier, doing so many
  624.  * comparisong and such.
  625.  */
  626. fill_in_sparse_array()
  627. {
  628.     int     ind;
  629.  
  630.     /*
  631.      * allocate space for our scratch space; it's initially
  632.      * 10 elements long, but can change in this routine if
  633.      * necessary
  634.      */
  635.     sp_array_size = 10;
  636.     sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
  637.  
  638.     /*
  639.      * there are at most five of these structures in the header
  640.      * itself; read these in first
  641.      */
  642.     for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
  643.         if (!head->header.sp[ind].numbytes)
  644.             break;
  645.         sparsearray[ind].offset =
  646.             from_oct(1+12, head->header.sp[ind].offset);
  647.         sparsearray[ind].numbytes =
  648.             from_oct(1+12, head->header.sp[ind].numbytes);
  649.     }
  650.     /*
  651.      * if the header's extended, we gotta read in exhdr's till
  652.      * we're done
  653.      */
  654.     if (head->header.isextended) {
  655.          /* how far into the sparsearray we are 'so far' */
  656.         static int so_far_ind = SPARSE_IN_HDR;    
  657.         union record *exhdr;
  658.            
  659.         for (;;) {
  660.         exhdr = findrec();
  661.         for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
  662.             if (ind+so_far_ind > sp_array_size-1) {
  663.                 /*
  664.                   * we just ran out of room in our
  665.                  *  scratch area - realloc it
  666.                   */
  667.                 sparsearray = (struct sp_array *)
  668.                     realloc(sparsearray, 
  669.                         sp_array_size*2*sizeof(struct sp_array));
  670.                 sp_array_size *= 2;
  671.             }
  672.             /*
  673.              * convert the character strings into longs
  674.              */
  675.             sparsearray[ind+so_far_ind].offset = 
  676.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
  677.             sparsearray[ind+so_far_ind].numbytes =
  678.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
  679.         }
  680.         /* 
  681.          * if this is the last extended header for this
  682.          * file, we can stop
  683.          */
  684.         if (!exhdr->ext_hdr.isextended)
  685.             break;
  686.         else {
  687.             so_far_ind += SPARSE_EXT_HDR;
  688.             userec(exhdr);
  689.         }
  690.         }
  691.         /* be sure to skip past the last one  */
  692.         userec(exhdr);
  693.     }
  694. }
  695.